﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections;

namespace Server
{
// test
    abstract class Server
    {
        protected IPAddress address_;
        protected int port_ = -1;
        protected IPEndPoint endPoint_;
        
        protected Socket listener_;
        protected Server(int port)
        {
            port_ = port;
        }
        public static IPAddress GetLocalIPAddress()
        {
            IPHostEntry host;

            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    return ip;
                }
            }

            return null;
        }
        public static string GetLocalIPString()
        {
            return GetLocalIPAddress().ToString();
        }
        public static string GetPublicIP()
        {
            String direction = "";
            WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
            using (WebResponse response = request.GetResponse())
            using (StreamReader stream = new StreamReader(response.GetResponseStream()))
            {
                direction = stream.ReadToEnd();
            }

            //Search for the ip in the html
            int first = direction.IndexOf("Address: ") + 9;
            int last = direction.LastIndexOf("</body>");
            direction = direction.Substring(first, last - first);

            return direction;
        }

        abstract public void Start();
        abstract public void Stop();
    }
    
    
    class WebServer
    {
        string WEB_SERVER_ROOT_PATH = "d:\\";
        string MIME_DATA_PATH = "\\Data\\mime\\mime.dat";
        string WorkingRootPath;
        TcpListener litener_;
        public int port = 5050;

        public WebServer()
        {
            try
            {
                
                char sep = Path.DirectorySeparatorChar;
                string CurrentDirectory = Directory.GetCurrentDirectory();
                WorkingRootPath = Directory.GetParent(CurrentDirectory).Parent.FullName;

                litener_ = new TcpListener(port);
                litener_.Start();
                Console.WriteLine("WEB_SERVER_ROOT_PATH: " + WEB_SERVER_ROOT_PATH);
                Console.WriteLine("WorkingRootPath: " + WorkingRootPath);
                Console.WriteLine("MIME_DATA_PATH: " + MIME_DATA_PATH);
                Console.WriteLine("HostName: " + Dns.GetHostName());
                Console.WriteLine("Local IP: " + Server.GetLocalIPString());
                Console.WriteLine("Public IP: " + Server.GetPublicIP());
                Console.WriteLine("Web Server Running... Press ^C to Stop...");

                
                Thread th = new Thread(new ThreadStart(StartListen));
                th.Start(); 

            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred while Listening :" + e.ToString());
            }
        }
        
        public void StartListen()
        {

            int iStartPos = 0;
            String sRequest;
            String sDirName;
            String sRequestedFile;
            String sErrorMessage;
            String sLocalDir;
            String sPhysicalFilePath = "";
            String sFormattedMessage = "";
            String sResponse = "";

            while (true)
            {
                
                Socket mySocket = litener_.AcceptSocket();

                Console.WriteLine("Socket Type " + mySocket.SocketType);
                if (mySocket.Connected) 
                {
                    Console.WriteLine("\n Client Connected!!\n==================\n CLient IP {0}\n", mySocket.RemoteEndPoint);

                    
                    Byte[] bReceive = new Byte[1024];
                    int i = mySocket.Receive(bReceive, bReceive.Length, 0);

                    string sBuffer = Encoding.UTF8.GetString(bReceive);
                    
                    if (sBuffer.Substring(0, 3) != "GET")
                    {
                        Console.WriteLine("Only Get Method is supported..: " + sBuffer);
                        mySocket.Close();
                        return;
                    }

                    iStartPos = sBuffer.IndexOf("HTTP", 1);
                    string sHttpVersion = sBuffer.Substring(iStartPos, 8);
                    sRequest = sBuffer.Substring(0, iStartPos - 1);
                    sRequest.Replace("\\", "/");

                    if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/")))
                    {
                        sRequest = sRequest + "/";
                    }
                    iStartPos = sRequest.LastIndexOf("/") + 1;
                    sRequestedFile = sRequest.Substring(iStartPos);

                    sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/") - 3);

                    if (sDirName == "/") 
                        sLocalDir = WEB_SERVER_ROOT_PATH;
                    else
                    {
                        string web_path = GetLocalPath(WEB_SERVER_ROOT_PATH);
                        if (string.Empty != web_path)
                            sLocalDir = web_path;
                        else
                            sLocalDir = sDirName;
                    }


                    Console.WriteLine("Directory Requested : " + sLocalDir);

                    
                    if (sLocalDir.Length == 0)
                    {
                        sErrorMessage = " Error!! Requested Directory does not exists";
                        SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket);


                        SendToBrowser(sErrorMessage, ref mySocket);

                        mySocket.Close();

                        continue;
                    }


                    String sMimeType = GetMimeType(sRequestedFile);
                    sPhysicalFilePath = sLocalDir + sRequestedFile;
                    Console.WriteLine("File Requested : " + sPhysicalFilePath);

                    if (File.Exists(sPhysicalFilePath) == false)
                    {

                        sErrorMessage = " 404 Error! File Does Not Exists...";
                        SendHeader(sHttpVersion, "", sErrorMessage.Length, " 404 Not Found", ref mySocket);
                        SendToBrowser(sErrorMessage, ref mySocket);

                        Console.WriteLine(sFormattedMessage);
                    }

                    else
                    {
                        int iTotBytes = 0;

                        sResponse = "";

                        FileStream fs = new FileStream(sPhysicalFilePath, FileMode.Open, FileAccess.Read,
                          FileShare.Read);


                        BinaryReader reader = new BinaryReader(fs);
                        byte[] bytes = new byte[fs.Length];
                        int read;
                        while ((read = reader.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            sResponse = sResponse + Encoding.UTF8.GetString(bytes, 0, read);
                            iTotBytes = iTotBytes + read;

                        }
                        reader.Close();
                        fs.Close();

                        SendHeader(sHttpVersion, sMimeType, iTotBytes, " 200 OK", ref mySocket);
                        
                        SendToBrowser(bytes, ref mySocket);
                    }
                    mySocket.Close();

                }
            }
        }
        public string GetMimeType(string ReqestFile)
        {
            return "";

            StreamReader sr;
            String Line = "";
            String MimeType = "";
            String FileExt = "";
            String MimeExt = "";

            
            ReqestFile = ReqestFile.ToLower();
            int iStartPos = ReqestFile.IndexOf(".");
            FileExt = ReqestFile.Substring(iStartPos);

            try
            {
                sr = new StreamReader(WEB_SERVER_ROOT_PATH + MIME_DATA_PATH);

                while ((Line = sr.ReadLine()) != null)
                {

                    Line.Trim();

                    if (Line.Length > 0)
                    {
                        iStartPos = Line.IndexOf(";");
                        Line = Line.ToLower();
                        MimeExt = Line.Substring(0, iStartPos);
                        MimeType = Line.Substring(iStartPos + 1);

                        if (MimeExt == FileExt)
                            break;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred : " + e.ToString());
            }

            if (MimeExt == FileExt)
                return MimeType;
            else
                return "";
        }
        public string GetLocalPath(string sDirName)
        {

            StreamReader sr;
            String Line = "";
            String sVirtualDir = "";
            String sRealDir = "";
            int iStartPos = 0;

            sDirName.Trim();
            WEB_SERVER_ROOT_PATH = WEB_SERVER_ROOT_PATH.ToLower();
            sDirName = sDirName.ToLower();


            try
            {
                sRealDir = WEB_SERVER_ROOT_PATH + sDirName.Replace("/", "\\");
            }
            catch (Exception e)
            {
                Console.WriteLine("An Exception Occurred : " + e.ToString());
            }

            if (Directory.Exists(sRealDir))
                return sRealDir;
            else
                return "";
        }
        public void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, ref Socket mySocket)
        {

            String sBuffer = "";

            // if Mime type is not provided set default to text/html
            if (sMIMEHeader.Length == 0)
            {
                sMIMEHeader = "text/html";  // Default Mime Type is text/html
            }

            sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
            sBuffer = sBuffer + "Server: cx1193719-b\r\n";
            sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
            sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
            sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";

            //Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer); 
            Byte[] bSendData = Encoding.UTF8.GetBytes(sBuffer);

            SendToBrowser(bSendData, ref mySocket);

            Console.WriteLine("Total Bytes : " + iTotBytes.ToString());

        }
        public void SendToBrowser(String sData, ref Socket mySocket)
        {
            SendToBrowser(Encoding.UTF8.GetBytes(sData), ref mySocket);
        }
        public void SendToBrowser(Byte[] bSendData, ref Socket mySocket)
        {
            int numBytes = 0;
            try
            {
                if (mySocket.Connected)
                {
                    if ((numBytes = mySocket.Send(bSendData, bSendData.Length, 0)) == -1)
                        Console.WriteLine("Socket Error cannot Send Packet");
                    else
                    {
                        Console.WriteLine("No. of bytes send {0}", numBytes);
                    }
                }
                else
                    Console.WriteLine("Connection Dropped....");
            }
            catch (Exception e)
            {
                Console.WriteLine("Error Occurred : {0} ", e);
            }
        }
     
       
    }
    namespace chatServerTest
    {
        class BroadcastServer
        {
            public static ArrayList socketList = new ArrayList(); //서버에 연결된 소켓들의 Array 입니다.
            private static BroadcastServer broadcastServer;
            private Thread threadWaitingSocket;
            private Socket listener;
            private int port;
            public static void start(int _port) //   서버 시작
            {
                if (broadcastServer == null)
                {
                    broadcastServer = new BroadcastServer(_port);
                }
            }
            public static void stop()       // 서버 종료
            {
                Console.WriteLine("서버를 종료합니다.");
                foreach (SocketHandler socketHandler in socketList)
                {
                    socketHandler.end();
                }
                if (broadcastServer != null)
                {
                    broadcastServer.threadStop();
                }
            }
            private void threadStop()
            {
                threadWaitingSocket.Abort();
                listener.Close();
            }
            private BroadcastServer(int _port)
            {
                port = _port;
                threadWaitingSocket = new Thread(new ThreadStart(WaitingSocket));
                threadWaitingSocket.Start();
            }
            private void WaitingSocket()    //  클라이언트의 접속을 체크하는 스레드.
            {
                IPAddress ipAddress = IPAddress.Any;
                IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);
                listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                listener.Bind(ipEndPoint);
                listener.Listen(1000);      //  동시에 접속할 접속자수를 설정
                Console.WriteLine("연결을 기다립니다.");
                while (true)
                {
                    Socket acceptedSocket = listener.Accept();      //  접속한 클라이언트 소켓
                    string acceptedIP = ((IPEndPoint)acceptedSocket.RemoteEndPoint).Address.ToString(); //  접속한 클라이언트 IP
                    socketList.Add(new SocketHandler(acceptedSocket));                                  //  접속리스트에 추가
                    Console.WriteLine(socketList.Count + "번째 컴퓨터 - " + acceptedIP + "에서 접속하였습니다.");
                }
            }
        }
        public class SocketHandler
        {
            public Socket socket;
            public Thread threadHandler;
            public SocketHandler(Socket socket)
            {
                this.socket = socket;
                threadHandler = new Thread(new ThreadStart(Handler));
                threadHandler.Start();
            }
            public void Handler()                   //  실질적인 서버작업
            {
                byte[] buffer = new byte[4096];
                int bufferCount;
                SendMsg((BroadcastServer.socketList.Count) + "명이 접속해 있습니다.");
                try
                {
                    while (true)
                    {
                        buffer.Initialize();
                        bufferCount = socket.Receive(buffer);
                        if (bufferCount == 0) break;
                        string Msgs = ASCIIEncoding.UTF8.GetString(buffer);
                        byte[] byteimsi = new byte[bufferCount];    // 남은버퍼 없애기용 임시byte
                        for (int i = 0; i < bufferCount; i++)
                        {
                            byteimsi[i] = buffer[i];
                        }
                        Msgs = ASCIIEncoding.UTF8.GetString(byteimsi);
                        Console.WriteLine("클라이언트에서 받은 메세지" + Msgs);
                        SendMsg(Msgs);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
                finally
                {
                    Console.WriteLine("클라이언트가 종료하였습니다.");
                    BroadcastServer.socketList.Remove(this);
                    socket.Close();
                    socket = null;
                }
            }
            public void SendMsg(string Msg)     //  메세지 보내기.
            {
                int bufferCount = 0;
                byte[] buffer = new byte[4096];
                buffer = ASCIIEncoding.UTF8.GetBytes(Msg);
                bufferCount = ASCIIEncoding.UTF8.GetByteCount(Msg);
                foreach (SocketHandler socketHandler in BroadcastServer.socketList)
                {
                    socketHandler.socket.Send(buffer, 0, bufferCount, SocketFlags.None);
                }
            }
            public void end()
            {
                threadHandler.Abort();
            }
        }
    }



}
